Skip to content

fix(assets-controller): heal native token assetsInfo entries#9099

Merged
salimtb merged 10 commits into
mainfrom
fix/heal-native-token-entries
Jun 12, 2026
Merged

fix(assets-controller): heal native token assetsInfo entries#9099
salimtb merged 10 commits into
mainfrom
fix/heal-native-token-entries

Conversation

@Prithpal-Sooriya

@Prithpal-Sooriya Prithpal-Sooriya commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Explanation

This commit introduces a new test case to ensure that the middleware correctly refetches asset metadata when the stored type is stale, even if an image is present. The test verifies that a previously misclassified native asset is updated to its correct type, enhancing the robustness of the asset handling logic.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Low Risk
Localized metadata correction on merge using existing type resolution; only touches assets in the incoming update and is covered by new tests.

Overview
AssetsController now self-heals stale assetsInfo.type values when applying pipeline updates. After merging incoming metadata, it walks asset IDs present in the current assetsInfo or assetsBalance payload, compares each entry’s type to #getAssetType(assetId), and rewrites mismatches (e.g. zero-address “native” tokens still labeled erc20 from older persisted state).

Healing runs on handleAssetsUpdate / #updateState paths, so a balance-only or metadata refresh can fix types without a separate migration. Genuine ERC-20s are left unchanged; normalization still applies so non-checksummed keys in responses reconcile against the canonical ID.

The changelog records the fix under Unreleased; tests cover native→erc20 healing via assetsInfo and assetsBalance, no-op for real ERC-20s, and checksum vs lowercase asset IDs.

Reviewed by Cursor Bugbot for commit 249ad86. Bugbot is set up for automated code reviews on this repo. Configure here.

… metadata

This commit introduces a new test case to ensure that the middleware correctly refetches asset metadata when the stored type is stale, even if an image is present. The test verifies that a previously misclassified native asset is updated to its correct type, enhancing the robustness of the asset handling logic.
@Prithpal-Sooriya Prithpal-Sooriya marked this pull request as ready for review June 11, 2026 22:39
@Prithpal-Sooriya Prithpal-Sooriya requested a review from a team as a code owner June 11, 2026 22:40
@Prithpal-Sooriya

Copy link
Copy Markdown
Contributor Author

@cursor READ AND FOLLOW THESE MEMORY FILES:

/babysit fix CI

@cursor

cursor Bot commented Jun 11, 2026

Copy link
Copy Markdown

Taking a look!

Open in Web Open in Cursor 

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7480667. Configure here.

Comment thread packages/assets-controller/src/AssetsController.ts
Comment thread packages/assets-controller/src/AssetsController.ts
Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
@cursor cursor Bot requested a review from a team as a code owner June 11, 2026 22:46
cursoragent and others added 4 commits June 11, 2026 22:48
Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
Comment on lines +2194 to +2196
const assetsInfoAssetIdsSet = new Set(
Object.keys(normalizedResponse.assetsInfo ?? {}) as Caip19AssetId[],
);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cursor I had a good back and forth with Claude around this.
The reason this works is because RPC detection can pick this up and therefore have some assetsInfo to rely on.

So as a safe measure we can use both the assetsInfo and assetsBalance.

        const assetsInfoAssetIdsSet = new Set([
          ...Object.keys(normalizedResponse.assetsInfo ?? {}),
          ...Object.entries(normalizedResponse.assetsBalance ?? {}).flatMap(
            ([_, b]) => Object.keys(b),
          ),
        ] as Caip19AssetId[]);

Thoughts?

@cursor cursor Bot Jun 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — I agree with your proposal, and I’ve already applied it.

Thoughts

Using both normalized assetsInfo and normalized assetsBalance keys is the safer behavior:

  • It preserves the assetsInfo-first logic.
  • It also heals stale types when a response only touches balance (which can happen via RPC detection flows).
  • Because both come from normalizedResponse, it avoids the non-checksummed key mismatch bug.

What I changed

Implemented reconciliation key set as union of:

  • Object.keys(normalizedResponse.assetsInfo ?? {})
  • asset IDs found in normalizedResponse.assetsBalance entries

Then reconciles metadata[assetId].type for all those touched IDs.

Added coverage

  • New test:
    reconciles a stale native type stored as erc20 when assetsBalance includes the asset
  • Existing normalized-key reconciliation test remains in place.

Changelog

Updated to reflect behavior now applies when asset appears in incoming:

  • assetsInfo or assetsBalance.

Validation run

Passed locally:

  • yarn lint:misc:check
  • yarn eslint packages/assets-controller/src/AssetsController.ts packages/assets-controller/src/AssetsController.test.ts
  • yarn workspace @metamask/assets-controller run jest --no-coverage src/AssetsController.test.ts --runInBand --forceExit
  • yarn changelog:validate

Git

Open in Web Open in Cursor 

cursoragent and others added 2 commits June 11, 2026 23:20
Co-authored-by: Prithpal Sooriya <prithpal.sooriya@gmail.com>
@salimtb salimtb added this pull request to the merge queue Jun 12, 2026
Merged via the queue into main with commit 80b5ef7 Jun 12, 2026
376 checks passed
@salimtb salimtb deleted the fix/heal-native-token-entries branch June 12, 2026 09:44
runway-github Bot added a commit to MetaMask/metamask-extension that referenced this pull request Jun 12, 2026
…healing metadata cp-13.35.0 (#43477)

## **Description**

Patch for this core fix: MetaMask/core#9099

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: fix(assets-controller): update patch to add
reconciliation/self-healing metadata

## **Related issues**

Fixes: #43352

## **Manual testing steps**

1. Start application on 13.34.0. Add IMX chain and token.
2. Inspect state -
`AssetsController.assetsInfo["eip155:13371/erc20:0x0000000000000000000000000000000000000000"]`
    - See that the token is marked as "erc20"
3. Start application with these changes
4. Inspect state -
`AssetsController.assetsInfo["eip155:13371/erc20:0x0000000000000000000000000000000000000000"]`
    - See that the token is marked as "native"

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

```json
{
    "aggregators": [],
    "decimals": 18,
    "erc20Permit": false,
    "image": "https://static.cx.metamask.io/api/v2/tokenIcons/assets/eip155/13371/erc20/0x0000000000000000000000000000000000000000.png",
    "name": "Immutable X",
    "occurrences": 100,
    "symbol": "IMX",
    "type": "erc20"
}
```

### **After**

```
{
    "aggregators": [],
    "decimals": 18,
    "erc20Permit": false,
    "image": "https://static.cx.metamask.io/api/v2/tokenIcons/assets/eip155/13371/erc20/0x0000000000000000000000000000000000000000.png",
    "name": "Immutable X",
    "occurrences": 100,
    "symbol": "IMX",
    "type": "native"
}
```

https://www.loom.com/share/45f25216181146a69fc4730786c8fec9

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes how persisted asset metadata types are updated across
websocket/RPC flows; incorrect classification could affect portfolio
display until the next reconcile, but the logic is narrowly corrective.
> 
> **Overview**
> Updates the **Yarn patch** for `@metamask/assets-controller@8.3.2` to
backport MetaMask/core#9099: asset metadata **types** are reconciled
when balance/info updates land, so mislabeled entries (e.g. IMX native
at the zero address stored as `erc20`) are corrected to `native`, `spl`,
or `erc20`.
> 
> The patch adds a **`getAssetType`** helper on `AssetsController` and
passes it into **BackendWebsocket**, **RPC**, and **price** data sources
(replacing the websocket’s `isNativeAsset` hook). During `_updateState`,
it walks asset IDs from incoming `assetsInfo` / `assetsBalance` and
**self-heals** `metadata[assetId].type` when it disagrees with
`getAssetType`, marking those IDs as changed metadata.
> 
> `yarn.lock` is refreshed for the new patch hash/checksum only.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
13e0626. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-extension that referenced this pull request Jun 12, 2026
…healing metadata cp-13.35.0 (#43477)

## **Description**

Patch for this core fix: MetaMask/core#9099

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: fix(assets-controller): update patch to add
reconciliation/self-healing metadata

## **Related issues**

Fixes: #43352

## **Manual testing steps**

1. Start application on 13.34.0. Add IMX chain and token.
2. Inspect state -
`AssetsController.assetsInfo["eip155:13371/erc20:0x0000000000000000000000000000000000000000"]`
    - See that the token is marked as "erc20"
3. Start application with these changes
4. Inspect state -
`AssetsController.assetsInfo["eip155:13371/erc20:0x0000000000000000000000000000000000000000"]`
    - See that the token is marked as "native"

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

```json
{
    "aggregators": [],
    "decimals": 18,
    "erc20Permit": false,
    "image": "https://static.cx.metamask.io/api/v2/tokenIcons/assets/eip155/13371/erc20/0x0000000000000000000000000000000000000000.png",
    "name": "Immutable X",
    "occurrences": 100,
    "symbol": "IMX",
    "type": "erc20"
}
```

### **After**

```
{
    "aggregators": [],
    "decimals": 18,
    "erc20Permit": false,
    "image": "https://static.cx.metamask.io/api/v2/tokenIcons/assets/eip155/13371/erc20/0x0000000000000000000000000000000000000000.png",
    "name": "Immutable X",
    "occurrences": 100,
    "symbol": "IMX",
    "type": "native"
}
```

https://www.loom.com/share/45f25216181146a69fc4730786c8fec9

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes how persisted asset metadata types are updated across
websocket/RPC flows; incorrect classification could affect portfolio
display until the next reconcile, but the logic is narrowly corrective.
>
> **Overview**
> Updates the **Yarn patch** for `@metamask/assets-controller@8.3.2` to
backport MetaMask/core#9099: asset metadata **types** are reconciled
when balance/info updates land, so mislabeled entries (e.g. IMX native
at the zero address stored as `erc20`) are corrected to `native`, `spl`,
or `erc20`.
>
> The patch adds a **`getAssetType`** helper on `AssetsController` and
passes it into **BackendWebsocket**, **RPC**, and **price** data sources
(replacing the websocket’s `isNativeAsset` hook). During `_updateState`,
it walks asset IDs from incoming `assetsInfo` / `assetsBalance` and
**self-heals** `metadata[assetId].type` when it disagrees with
`getAssetType`, marking those IDs as changed metadata.
>
> `yarn.lock` is refreshed for the new patch hash/checksum only.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
13e0626. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot pushed a commit to MetaMask/metamask-extension that referenced this pull request Jun 12, 2026
…add reconciliation/self-healing metadata cp-13.35.0 (#43477)

## **Description**

Patch for this core fix: MetaMask/core#9099

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: fix(assets-controller): update patch to add
reconciliation/self-healing metadata

## **Related issues**

Fixes: #43352

## **Manual testing steps**

1. Start application on 13.34.0. Add IMX chain and token.
2. Inspect state -
`AssetsController.assetsInfo["eip155:13371/erc20:0x0000000000000000000000000000000000000000"]`
    - See that the token is marked as "erc20"
3. Start application with these changes
4. Inspect state -
`AssetsController.assetsInfo["eip155:13371/erc20:0x0000000000000000000000000000000000000000"]`
    - See that the token is marked as "native"

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

```json
{
    "aggregators": [],
    "decimals": 18,
    "erc20Permit": false,
    "image": "https://static.cx.metamask.io/api/v2/tokenIcons/assets/eip155/13371/erc20/0x0000000000000000000000000000000000000000.png",
    "name": "Immutable X",
    "occurrences": 100,
    "symbol": "IMX",
    "type": "erc20"
}
```

### **After**

```
{
    "aggregators": [],
    "decimals": 18,
    "erc20Permit": false,
    "image": "https://static.cx.metamask.io/api/v2/tokenIcons/assets/eip155/13371/erc20/0x0000000000000000000000000000000000000000.png",
    "name": "Immutable X",
    "occurrences": 100,
    "symbol": "IMX",
    "type": "native"
}
```

https://www.loom.com/share/45f25216181146a69fc4730786c8fec9

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes how persisted asset metadata types are updated across
websocket/RPC flows; incorrect classification could affect portfolio
display until the next reconcile, but the logic is narrowly corrective.
> 
> **Overview**
> Updates the **Yarn patch** for `@metamask/assets-controller@8.3.2` to
backport MetaMask/core#9099: asset metadata **types** are reconciled
when balance/info updates land, so mislabeled entries (e.g. IMX native
at the zero address stored as `erc20`) are corrected to `native`, `spl`,
or `erc20`.
> 
> The patch adds a **`getAssetType`** helper on `AssetsController` and
passes it into **BackendWebsocket**, **RPC**, and **price** data sources
(replacing the websocket’s `isNativeAsset` hook). During `_updateState`,
it walks asset IDs from incoming `assetsInfo` / `assetsBalance` and
**self-heals** `metadata[assetId].type` when it disagrees with
`getAssetType`, marking those IDs as changed metadata.
> 
> `yarn.lock` is refreshed for the new patch hash/checksum only.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
13e0626. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants